﻿using System;
using System.Collections.Generic;
using System.Text;
using NUnit.Framework;

namespace memcached
{
    public abstract class MemCacheTestBase
    {
        [Test]
        public void SetGet()
        {
            ICache m = GetCache();
            string key = GetKey();

            Assert.IsTrue(m.Set(key, "b"), "Set");

            bool s;
            object v = m.Get(key, out s);
            Assert.IsTrue(s);
            Assert.IsTrue("b".Equals(v), "Get");

            Assert.IsTrue(m.Set(key, "c"), "Set");

            v = m.Get(key, out s);
            Assert.IsTrue(s);
            Assert.IsTrue("c".Equals(v), "Get");
        }

        [Test]
        public void Remove()
        {
            ICache m = GetCache();
            string key = GetKey();
            Assert.IsTrue(m.Set(key, "b"), "Set");

            Assert.IsTrue(m.Remove(key));
            bool s;
            object v = m.Get(key, out s);
            Assert.IsTrue(s);
            Assert.IsTrue(v == null, "Get");
        }

        [Test]
        public void ExpireAt()
        {
            ICache m = GetCache();
            string key = GetKey();
            bool s1 = m.Set(key, DateTime.Now.AddMilliseconds(1100), "b");
            bool s2;
            object v = m.Get(key, out s2);
            Assert.IsTrue(s1, "Set");
            Assert.IsTrue(s2);
            Assert.IsTrue("b".Equals(v), "Get");

            System.Threading.Thread.Sleep(Wait + 500);

            v = m.Get(key, out s1);
            Assert.IsTrue(s1);
            Assert.IsTrue(v == null, "Get");
        }

        [Test]
        public void ValidFor()
        {
            ICache m = GetCache();
            string key = GetKey();
            Assert.IsTrue(m.Set(key, 1, "b"), "Set");
            bool s;
            object v = m.Get(key, out s);
            Assert.IsTrue(s);
            Assert.IsTrue("b".Equals(v), "Get");

            System.Threading.Thread.Sleep(Wait);

            v = m.Get(key, out s);
            Assert.IsTrue(s);
            Assert.IsTrue(v == null, "Get");
        }

        [Test]
        public void AddValidFor()
        {
            ICache m = GetCache();
            string key = GetKey();
            Assert.IsTrue(m.Add(key, 1, "b"), "Add");

            System.Threading.Thread.Sleep(Wait);
            bool s;
            object v = m.Get(key, out s);
            Assert.IsTrue(s);
            Assert.IsTrue(v == null, "Get");
        }

        [Test]
        public void AddExpireAt()
        {
            ICache m = GetCache();
            string key = GetKey();
            Assert.IsTrue(m.Add(key, DateTime.Now.AddMilliseconds(300), "b"), "Add");

            System.Threading.Thread.Sleep(Wait);
            bool s;
            object v = m.Get(key, out s);
            Assert.IsTrue(s);
            Assert.IsTrue(v == null, "Get");
        }

        [Test]
        public void AddExisting()
        {
            ICache m = GetCache();
            string key = GetKey();
            Assert.IsTrue(m.Set(key, "a"));

            Assert.IsTrue(!m.Add(key, 1, "b"), "Add");
            bool s;
            object v = m.Get(key, out s);
            Assert.IsTrue(s);
            Assert.IsTrue("a".Equals(v), "Get");
        }

        [Test]
        public void AddExpired()
        {
            ICache m = GetCache();
            string key = GetKey();
            Assert.IsTrue(m.Set(key, DateTime.Now.AddMilliseconds(200), "a"));

            System.Threading.Thread.Sleep(Wait);

            Assert.IsTrue(m.Add(key, 2, "b"), "Add");// item expired so allow

            System.Threading.Thread.Sleep(100);
            bool s;
            object v = m.Get(key, out s);
            Assert.IsTrue(s);
            Assert.IsTrue("b".Equals(v), "Get");
        }

        #region Housekeeping

        protected abstract ICache GetCache();

        protected static string GetKey()
        {
            return (DateTime.Now.Ticks + key++).ToString();
        }
        private static int key;

        protected virtual int Wait
        {
            get { return 1300; }
        }

        public virtual void Run()
        {
            var mi = GetType().GetMethods(
                System.Reflection.BindingFlags.Instance
                | System.Reflection.BindingFlags.Public);
            foreach (var m in mi)
                if (m.Name != "Run" &&
                    m.Name != "ToString" &&
                    m.Name != "Equals" &&
                    m.Name != "GetHashCode" &&
                    m.Name != "GetType")
                    m.Invoke(this, null);
        }
        #endregion
    }

    public abstract class MemCacheTestBaseExt : MemCacheTestBase
    {
        [Test]
        public virtual void Interop()
        {
            ICacheAdvanced m = (ICacheAdvanced)GetCache();
            string key = GetKey();
            if (m is RedisCache)
                Assert.IsTrue(((RedisCache)m).Client.Set<int>(key, 1), "Set");
            else
                Assert.IsTrue(m.Set(key, "1"), "Set");

            long v = m.Increment(key, 111);
            Assert.IsTrue(v == 2);

            v = m.Decrement(key);
            Assert.IsTrue(v == 1);

            v = m.Decrement(key);
            Assert.IsTrue(v == 0);

            v = m.Decrement(key);
            Assert.IsTrue(v <= 0);

            key = GetKey();
            v = m.Decrement(key);
            Assert.IsTrue(v == -1);
        }

        [Test]
        public virtual void SetCasInvalid()
        {
            ICacheAdvanced m = (ICacheAdvanced)GetCache();
            string key = GetKey();
            try
            {
                Assert.IsTrue(!m.Set(key, (ulong)100, "b"), "Set");
                bool s;
                object v = m.Get(key, out s);
                Assert.IsTrue(s);
                Assert.IsTrue(v == null, "Get");
            }
            catch (NotImplementedException) // for Redis
            {
            }
        }

        [Test]
        public virtual void SetCas()
        {
            ICacheAdvanced m = (ICacheAdvanced)GetCache();
            string key = GetKey();
            Assert.IsTrue(m.Set(key, "b"), "Set");
            try
            {
                CasValue<string> v = m.Get<string>(key);
                Assert.IsTrue(v != null, "GetCas");
                Assert.IsTrue(v.Value == "b", "GetCas");
                Assert.IsTrue(v.CAS > 0, "GetCas");

                Assert.IsTrue(m.Set(key, v.CAS, "c"), "SetCas");

                CasValue<string> v1 = m.Get<string>(key);
                Assert.IsTrue(v1 != null, "GetCas");
                Assert.IsTrue(v1.Value == "c", "GetCas");
                Assert.IsTrue(v1.CAS != v.CAS, "GetCas");
            }
            catch (NotImplementedException) // for Redis
            {
            }
        }

        [Test]
        public virtual void SetCasExpire()
        {
            ICacheAdvanced m = (ICacheAdvanced)GetCache();
            string key = GetKey();
            Assert.IsTrue(m.Set(key, DateTime.Now.AddMilliseconds(500), "b"), "Set");

            System.Threading.Thread.Sleep(600);
            try
            {
                CasValue<string> v = m.Get<string>("a");
                Assert.IsTrue(v == null, "GetCas");
            }
            catch (NotImplementedException) // for Redis
            {
            }
        }

        [Test]
        public virtual void SetCasValidFor()
        {
            ICacheAdvanced m = (ICacheAdvanced)GetCache();
            string key = GetKey();
            Assert.IsTrue(((ICache)m).Set(key, (int)1, "b"), "Set");

            System.Threading.Thread.Sleep(1200);
            try
            {
                CasValue<string> v = m.Get<string>(key);
                Assert.IsTrue(v == null, "GetCas");
            }
            catch (NotImplementedException) // for Redis
            {
            }
        }
    }
}